#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import sys

from common.common import exter_attack, output_result_file, report_job_details
from common.common_models import SubJobDetails, LogDetail
from common.const import SysData, ExecuteResultEnum, SubJobStatusEnum
from common.exception.common_exception import ErrCodeException
from common.job_const import ParamKeyConst
from common.parse_parafile import ParamFileUtil
from common.util.check_utils import is_valid_uuid, is_valid_id
from gaussdbt.single import LOGGER
from gaussdbt.single.comm.gst_single_const import RestoreType, GstSingleKeyConst, GstSingleConst, ArchiveType
from gaussdbt.single.comm.utils.gst_single_comm_utils import GstSingleCommonUtil
from gaussdbt.single.comm.utils.gst_single_param_utils import GstSingleParamUtil
from gaussdbt.single.restore.single_full_restore import SingleFullRestore
from gaussdbt.single.restore.single_log_restore import SingleLogRestore


class RestoreApi:
    @staticmethod
    @exter_attack
    def allow_restore_in_local_node(pid, job_id, sub_job_id):
        # 获取恢复实例
        restore_instance = RestoreApi.get_restore_instance(pid, job_id, sub_job_id)
        action_result = restore_instance.allow_restore_in_local_node()
        output_result_file(pid, action_result)

    @staticmethod
    @exter_attack
    def exec_restore_pre(pid, job_id, sub_job_id):
        # 获取恢复实例
        restore_instance = RestoreApi.get_restore_instance(pid, job_id, sub_job_id)
        restore_instance.exec_restore_pre()

    @staticmethod
    @exter_attack
    def exec_restore(pid, job_id, sub_job_id):
        restore_instance = RestoreApi.get_restore_instance(pid, job_id, sub_job_id)
        job_param = restore_instance.job_param
        # 如果是新位置恢复，要校验GSDB_DATA目录是否一致，如果不一致给提示
        target_location = job_param.get(ParamKeyConst.JOB).get(ParamKeyConst.EXT_INFO,
                                                               {}).get(ParamKeyConst.RESTORE_TARGET_LOCATION)
        if target_location == "new":
            copies = job_param['job']['copies']
            source_gsdb_data_dir = RestoreApi.get_source_gsdb_data_dir(copies[len(copies) - 1])
            # GsdbDataDir字段有就校验
            if source_gsdb_data_dir:
                os_user = GstSingleParamUtil.get_os_user("restore", pid, is_restore=True)
                target_gsdb_data_dir = GstSingleCommonUtil.get_val_of_gsdb_env_var(os_user, "GSDB_DATA")
                if source_gsdb_data_dir != target_gsdb_data_dir:
                    log_detail = LogDetail(logInfo=GstSingleConst.RESTORE_DIFFERENT_INSTALL_DIR_WARN_LABEL,
                                           logInfoParam=[source_gsdb_data_dir, target_gsdb_data_dir], logLevel=2)
                    report_job_details(pid,
                                       SubJobDetails(taskId=job_id, subTaskId=sub_job_id, progress=100,
                                                     logDetail=[log_detail],
                                                     taskStatus=SubJobStatusEnum.RUNNING.value))
        restore_instance.exec_restore()

    @staticmethod
    def get_source_gsdb_data_dir(copy):
        """
        获取恢复副本本身的gsdb_data_dir
        """
        copy_type = copy.get("type", "")
        if copy_type in ArchiveType.archive_array:
            # 如果是归档到磁带副本，需要从extendInfo里拿原始备份副本
            return copy.get("extendInfo", {}).get("extendInfo", {}).get(GstSingleKeyConst.GSDB_DATA_DIR, "")
        return copy.get("extendInfo", {}).get(GstSingleKeyConst.GSDB_DATA_DIR, "")

    @staticmethod
    @exter_attack
    def exec_restore_post(pid, job_id, sub_job_id):
        restore_instance = RestoreApi.get_restore_instance(pid, job_id, sub_job_id)
        restore_instance.exec_restore_post()

    @staticmethod
    def get_restore_instance(pid, job_id, sub_job_id):
        job_param = ParamFileUtil.parse_param_file_and_valid_by_schema(pid, GstSingleConst.JSON_SCHEMA_PATH)
        restore_timestamp = job_param.get(ParamKeyConst.JOB, {}).get(ParamKeyConst.EXT_INFO,
            {}).get(ParamKeyConst.RESTORE_TIMESTAMP)
        restore_type = RestoreType.FULL_RESTORE
        if restore_timestamp:
            LOGGER.info("The current restore job type is log restore")
            restore_type = RestoreType.TIMESTAMP_RESTORE
        instance_type = {
            RestoreType.FULL_RESTORE: SingleFullRestore,
            RestoreType.TIMESTAMP_RESTORE: SingleLogRestore
        }
        return instance_type.get(restore_type)(pid, job_id, sub_job_id, job_param)

FUNC_DICT = {
    'AllowRestoreInLocalNode': RestoreApi.allow_restore_in_local_node,
    'RestorePrerequisite': RestoreApi.exec_restore_pre,
    'Restore': RestoreApi.exec_restore,
    'RestorePostJob': RestoreApi.exec_restore_post
}


def restore_main(restore_args):
    """
    功能描述：主任务执行
    参数：
    @fun_type：argv[0] 方法类型
    @pid：argv[1] 请求ID
    @job_id：argv[2] 主任务ID
    @sub_job_id: argv[3] 子任务ID
    例：python3 api.py fun_type pid job_id sub_job_id
    """

    fun_type = restore_args[0]
    pid = restore_args[1]
    job_id = ''
    sub_job_id = ''
    if len(restore_args) > 2:
        job_id = restore_args[2]
        if not is_valid_uuid(job_id):
            LOGGER.error('Job id (%s) is not valid', job_id)
            return False
    if len(restore_args) > 3:
        sub_job_id = restore_args[3]
        if not is_valid_uuid(sub_job_id):
            LOGGER.error('Sub job id (%s) is not valid', sub_job_id)
            return False
    LOGGER.info('######## Function: %s start, pid=%s, jobId=%s, subJobId=%s', fun_type, pid, job_id, sub_job_id)
    # 校验pid,job_id,sub_job_id
    if not is_valid_id(pid):
        LOGGER.warn(f'pid is invalid!')
        sys.exit(1)
    if not is_valid_id(job_id):
        LOGGER.warn(f'job_id is invalid!')
        sys.exit(1)
    if not is_valid_id(sub_job_id):
        LOGGER.warn(f'sub_job_id is invalid!')
        sys.exit(1)
    try:
        FUNC_DICT[fun_type](pid, job_id, sub_job_id)
    except KeyError:
        LOGGER.error('######## Function: %s not supported', fun_type)
        return False
    except ErrCodeException as errcode_str:
        LOGGER.exception(errcode_str)
        LOGGER.error('######## Function: %s exception', fun_type)
        return False
    except Exception as exception_str:
        LOGGER.exception(exception_str)
        LOGGER.error('######## Function: %s exception', fun_type)
        return False
    LOGGER.info('######## Function: %s end, pid: %s', fun_type, pid)
    return True


if __name__ == '__main__':
    SysData.SYS_STDIN = sys.stdin.readline()
    args = sys.argv[1:]
    ret = restore_main(args)
    if not ret:
        exit(ExecuteResultEnum.INTERNAL_ERROR)